
package com.bluelinelabs.conductor.conductor.changehandler;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.render.Canvas;

import com.bluelinelabs.conductor.conductor.ControllerChangeHandler;


/**
 * lipeiquan
 *
 * @since 2021-05-06
 */
public abstract class AnimatorChangeHandler extends ControllerChangeHandler {

    private static final String KEY_DURATION = "AnimatorChangeHandler.duration";
    private static final String KEY_REMOVES_FROM_ON_PUSH = "AnimatorChangeHandler.removesFromViewOnPush";

    @SuppressWarnings("WeakerAccess")
    public static final long DEFAULT_ANIMATION_DURATION = -1;

    private long animationDuration;
    boolean removesFromViewOnPush;
    boolean canceled;
    boolean needsImmediateCompletion;
    private boolean completed;
    AnimatorGroup animator;
    private OnAnimationReadyOrAbortedListener onAnimationReadyOrAbortedListener;


    @SuppressWarnings("WeakerAccess")
    public AnimatorChangeHandler() {
        this(DEFAULT_ANIMATION_DURATION, true);
    }

    @SuppressWarnings("WeakerAccess")
    public AnimatorChangeHandler(boolean removesFromViewOnPush) {
        this(DEFAULT_ANIMATION_DURATION, removesFromViewOnPush);
    }

    @SuppressWarnings("WeakerAccess")
    public AnimatorChangeHandler(long duration) {
        this(duration, true);
    }

    public AnimatorChangeHandler(long duration, boolean removesFromViewOnPush) {
        animationDuration = duration;
        this.removesFromViewOnPush = removesFromViewOnPush;
    }

    protected abstract AnimatorGroup getAnimator(ComponentContainer container, Component from, Component to, boolean isPush, boolean toAddedToContainer);

    protected abstract void resetFromView(Component from);

    public long getAnimationDuration() {
        return animationDuration;
    }


    @Override
    public void performChange(ComponentContainer container, Component from, Component to, boolean isPush, ControllerChangeCompletedListener changeListener) {

        boolean readyToAnimate = true;
        final boolean addingToView = to != null && to.getComponentParent() == null;


        if (addingToView) {
            if (isPush || from == null) {
                if (removesFromViewOnPush) {
                    container.addComponent(to, 0);
                } else {
                    container.addComponent(to);
                }
            } else if (to.getComponentParent() == null) {
                container.addComponent(to, container.getChildIndex(from));
            }

            if (to.getWidth() <= 0 && to.getHeight() <= 0) {
                readyToAnimate = false;

                onAnimationReadyOrAbortedListener = new OnAnimationReadyOrAbortedListener(container, from, to, isPush, true, changeListener);
                to.addDrawTask(onAnimationReadyOrAbortedListener);

            }
        }

        if (readyToAnimate) {
            performAnimation(container, from, to, isPush, addingToView, changeListener);
        }

    }

    void performAnimation(final ComponentContainer container, final Component from, final Component to, final boolean isPush, final boolean toAddedToContainer, final ControllerChangeCompletedListener changeListener) {

        if (canceled) {
            complete(changeListener, null);
            return;
        }
        if (needsImmediateCompletion) {
            if (from != null && (!isPush || removesFromViewOnPush)) {
                container.removeComponent(from);
            }
            complete(changeListener, null);
            if (isPush && from != null) {
                resetFromView(from);
            }
            return;
        }


        animator = getAnimator(container, from, to, isPush, toAddedToContainer);


        if (animationDuration > 0) {
            animator.setDuration(animationDuration);
        }

        animator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

                if (from != null) {
                    resetFromView(from);
                }

                if (to != null && to.getComponentParent() == container) {
                    container.removeComponent(to);
                }

                complete(changeListener, this);
            }

            @Override
            public void onEnd(Animator animator) {

                if (!canceled && animator != null) {
                    if (from != null && (!isPush || removesFromViewOnPush)) {
                        container.removeComponent(from);
                    }

                    complete(changeListener, this);

                    if (isPush && from != null) {
                        resetFromView(from);
                    }
                }
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });


        animator.start();

    }


    void complete(ControllerChangeCompletedListener changeListener, Animator.StateChangedListener animatorListener) {

        if (!completed) {
            completed = true;
            changeListener.onChangeCompleted();
        }

        if (animator != null) {
            if (animatorListener != null) {
//                animatorListener.onCancel(animator);
            }
            animator.cancel();
            animator = null;
        }

        onAnimationReadyOrAbortedListener = null;

    }


    private class OnAnimationReadyOrAbortedListener implements Component.DrawTask {
        final ComponentContainer container;
        final Component from;
        final Component to;
        final boolean isPush;
        final boolean addingToView;
        final ControllerChangeCompletedListener changeListener;
        private boolean hasRun;

        OnAnimationReadyOrAbortedListener(ComponentContainer container, Component from, Component to, boolean isPush, boolean addingToView, ControllerChangeCompletedListener changeListener) {
            this.container = container;
            this.from = from;
            this.to = to;
            this.isPush = isPush;
            this.addingToView = addingToView;
            this.changeListener = changeListener;
        }

        void onReadyOrAborted() {
            if (!hasRun) {
                hasRun = true;

                if (to != null) {
//                    final ViewTreeObserver observer = to.getViewTreeObserver();
//                    if (observer.isAlive()) {
//                        observer.removeOnPreDrawListener(this);
//                    }
                }

                performAnimation(container, from, to, isPush, addingToView, changeListener);
            }
        }

        @Override
        public void onDraw(Component component, Canvas canvas) {

            onReadyOrAborted();
        }
    }
}
